package com.example.dakotanews.utils;

import com.example.dakotanews.dto.utils.ResponseCode;
import com.example.dakotanews.entity.userInfo.JWTUserInfo;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

@Component
public class JWTAuthenticationFilter extends OncePerRequestFilter {

    private final JwtUtil jwtUtil;
    private final AuthenticationHandler authenticationHandler;

    public JWTAuthenticationFilter(
            JwtUtil jwtUtil,
            AuthenticationHandler authenticationHandler
            ) {
        this.jwtUtil = jwtUtil;
        this.authenticationHandler = authenticationHandler;
    }


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        // 定义非完全私有接口白名单（包含公有接口和半私有接口）
        // 现在该名单已经不局限于非完全私有接口了，因为检验JWT的操作放在了控制层。
        // 所有接口都需要添加到该名单中
        // 该过滤器的意义是，拦截非系统设定的路径的请求，防止出现意料之外的请求。
        String[] publicPaths = {
                "/dakota/api/user/login",
                "/dakota/api/user/follow",
                "/dakota/api/user/getUserInfo",
                "/dakota/api/user/getPropertyInfo",
                "/dakota/api/news/getDetail",
                "/dakota/api/news/operateNews",
                "/dakota/api/news/getSorts",
                "/dakota/api/news/getRecommends",
                "/dakota/api/news/getNewsEditForm",
                "/dakota/api/news/publishNews",
                "/dakota/api/comment/getNewsComments",
                "/dakota/api/comment/commit",
                "/dakota/api/comment/support",
                "/dakota/api/issue/getAllIssues",
                "/dakota/api/issue/addIssue",
        };

        // 此处获取的uri包含SpringBoot配置中的路径前缀
        String uri = request.getRequestURI();

        // 若访问的接口路径在公有接口路径数组中
        for(String publicPath : publicPaths){
            if(uri.matches(publicPath)) {
                // 直接放行
                filterChain.doFilter(request, response);
                return;
            }
        }

        // 否则访问的是完全私有接口
        // 判断请求头是否携带token
        String token = request.getHeader("Authorization");
        JWTUserInfo jwtUserInfo;
        if(!StringUtils.hasText(token)) {
            // 未登录用户访问不公开的接口的处理
            authenticationHandler.authHandler(response, HttpServletResponse.SC_UNAUTHORIZED, ResponseCode.FORBIDDEN, "未授权的请求", "请登录");
            return;
        } else {
            try {
                // 解析token
                // Integer userId = Integer.valueOf(jwtUtil.extractSubject(token));
                jwtUserInfo = jwtUtil.extractJwtUserInfo(token);
            } catch (JwtException ex) {
                // 解析时出现错误
                // ex.printStackTrace();
                if(ex instanceof ExpiredJwtException) {
                    System.out.println("ExpiredJwtException: 试图解析过期的令牌");
                    authenticationHandler.authHandler(response, HttpServletResponse.SC_UNAUTHORIZED, ResponseCode.UNAUTHORIZED, "授权已过期", "请重新登录");
                } else if (ex instanceof SignatureException) {
                    System.out.println("ExpiredJwtException: 试图解析无效的令牌");
                    authenticationHandler.authHandler(response, HttpServletResponse.SC_FORBIDDEN, ResponseCode.FORBIDDEN, "签名无效", "请登录");
                } else if(ex instanceof MalformedJwtException) {
                    System.out.println("ExpiredJwtException: 试图解析JWT格式不正确的令牌");
                    authenticationHandler.authHandler(response, HttpServletResponse.SC_FORBIDDEN, ResponseCode.FORBIDDEN, "令牌格式不正确", "请登录");
                }
                return;
            }

            // TODO 使用token携带的用户（部分）信息
        }

        // 将解析token获取到的用户信息放入spring security的上下文
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(jwtUserInfo, null, null);
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);

        filterChain.doFilter(request, response);

    }

}
